<?php

namespace think\filesystem\Adapter;

use League\Flysystem\Config;
use League\Flysystem\FileAttributes;
use League\Flysystem\FilesystemAdapter;
use Qiniu\Auth;
use Qiniu\Cdn\CdnManager;
use Qiniu\Storage\BucketManager;
use Qiniu\Storage\UploadManager;

class QiniuAdapter implements FilesystemAdapter
{

    /**
     * @var Auth
     */
    protected $authManager;

    /**
     * @var UploadManager
     */
    protected $uploadManager;

    /**
     * @var BucketManager
     */
    protected $bucketManager;

    /**
     * @var CdnManager
     */
    protected $cdnManager;

    /**
     * 配置参数
     * @var array
     */
    protected $config = [
        "accessKey" => "",
        "secretKey" => "",
        "bucket"    => "",
        "domain"    => "https://up-z1.qiniup.com",
        "cdn"       => ""
    ];

    /**
     * 初始化
     * @param $config
     */
    public function __construct($config)
    {
        $this->config = $config;
    }

    /**
     * 上传文件
     * @param string $path
     * @param string $contents
     * @param Config $config
     * @return void
     */
    public function write(string $path, string $contents, Config $config): void
    {
        $mime    = $config->get('mime', 'application/octet-stream');
        $upToken = $this->getAuthManager()->uploadToken($this->config['bucket']);
        $this->getUploadManager()->put(
            $upToken,
            $path,
            $contents,
            null,
            $mime,
            $path
        );
    }

    /**
     * 写入文件流
     * @param string $path
     * @param $contents
     * @param Config $config
     * @return void
     */
    public function writeStream(string $path, $contents, Config $config): void
    {
        $this->write($path, \stream_get_contents($contents), $config);
    }

    /**
     * 移动文件
     * @param string $source
     * @param string $destination
     * @param Config $config
     * @return void
     */
    public function move(string $source, string $destination, Config $config): void
    {
        $this->getBucketManager()->rename($this->config['bucket'], $source, $destination);
    }

    /**
     * 拷贝文件
     * @param string $source
     * @param string $destination
     * @param Config $config
     * @return void
     */
    public function copy(string $source, string $destination, Config $config): void
    {
        $this->getBucketManager()->copy($this->config['bucket'], $source, $this->config['bucket'], $destination);
    }

    /**
     * 删除文件
     * @param string $path
     * @return void
     */
    public function delete(string $path): void
    {
        $this->getBucketManager()->delete($this->config['bucket'], $path);
    }

    /**
     * 删除目录
     * @param string $path
     * @return void
     */
    public function deleteDirectory(string $path): void
    {
    }

    /**
     * 创建目录
     * @param string $path
     * @param Config $config
     * @return void
     */
    public function createDirectory(string $path, Config $config): void
    {
    }

    /**
     * 文件是否存在
     * @param string $path
     * @return bool
     */
    public function fileExists(string $path): bool
    {
        [, $error] = $this->getBucketManager()->stat($this->config['bucket'], $path);
        return is_null($error);
    }

    /**
     * 读取文件
     * @param string $path
     * @return string
     */
    public function read(string $path): string
    {
        return file_get_contents($this->getUrl($path));
    }

    /**
     * 读取文件流
     * @param string $path
     * @return resource
     */
    public function readStream(string $path)
    {
        return fopen($this->getUrl($path), 'r');
    }

    /**
     * 获取指定目录下文件列表
     * @param string $path
     * @param bool $deep
     * @return iterable
     */
    public function listContents(string $path, bool $deep): iterable
    {
        $result = $this->getBucketManager()->listFiles($this->config['bucket'], $path);
        foreach ($result[0]['items'] ?? [] as $file) {
            yield $this->normalizeFileInfo($file);
        }
    }

    /**
     * 获取metadata信息
     * @param string $path
     * @return FileAttributes
     */
    public function mimeType(string $path): FileAttributes
    {
        return $this->getMetadata($path);
    }

    /**
     * 文件meta信息
     * @param string $path
     * @return FileAttributes
     */
    public function fileSize(string $path): FileAttributes
    {
        return $this->getMetadata($path);
    }

    /**
     * 上次修改信息
     * @param string $path
     * @return FileAttributes
     */
    public function lastModified(string $path): FileAttributes
    {
        return $this->getMetadata($path);
    }

    public function visibility(string $path): FileAttributes
    {
        throw new \Exception($path);
    }

    public function setVisibility(string $path, string $visibility): void
    {

    }

    /**
     * 格式化url地址
     * @param string $path
     * @return string
     */
    public function getUrl(string $path): string
    {
        return $this->config['cdn'] . "/" . $path;
    }

    /**
     * 获取配置参数
     * @return array
     */
    public function getConfig()
    {
        return $this->config;
    }

    /**
     * 从指定URL抓取资源，并将该资源存储到指定空间中
     * @param string $url
     * @param string $path 指定为"source"时按目标路径存储
     * @return string
     */
    public function fetch(string $url, string $path = 'source')
    {
        ini_set("user_agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/102.0.0.0 Safari/537.36");
        $parsedUrl = parse_url($url);
        $filePath  = ltrim(pathinfo($parsedUrl['path'], PATHINFO_DIRNAME), '/'); //文件路径
        $fileName  = pathinfo($parsedUrl['path'], PATHINFO_BASENAME);            //文件名
        //$fileExtension = pathinfo($parsedUrl['path'], PATHINFO_EXTENSION); //文件后缀
        $path = $path == "source" ? $filePath . '/' . $fileName : $path;
        $this->getBucketManager()->fetch($url, $this->config['bucket'], $path);
        return $path;
    }

    /**
     * 获取BucketManager
     * @return BucketManager
     */
    public function getBucketManager()
    {
        return $this->bucketManager ?: $this->bucketManager = new BucketManager($this->getAuthManager());
    }

    /**
     * 获取authManager
     * @return Auth
     */
    public function getAuthManager()
    {
        return $this->authManager ?: $this->authManager = new Auth($this->config['accessKey'], $this->config['secretKey']);
    }

    /**
     * 获取UploadManager
     * @return UploadManager
     */
    public function getUploadManager()
    {
        return $this->uploadManager ?: $this->uploadManager = new UploadManager();
    }

    /**
     * 获取CdnManager
     * @return CdnManager
     */
    public function getCdnManager()
    {
        return $this->cdnManager ?: $this->cdnManager = new CdnManager($this->getAuthManager());
    }

    /**
     * 生成uploadToken
     * @param $key
     * @return string
     */
    public function getUploadToken($key)
    {
        return $this->getAuthManager()->uploadToken($this->config['bucket'], $key);
    }

    /**
     * 文件meta信息
     * @param $path
     * @return FileAttributes
     */
    public function getMetadata($path)
    {
        $result           = $this->getBucketManager()->stat($this->config['bucket'], $path);
        $result[0]['key'] = $path;
        return $this->normalizeFileInfo($result[0]);
    }

    /**
     * 获取文件信息 转数组：fileAttributes->jsonSerialize()
     * @param array $stats
     * @return FileAttributes
     */
    protected function normalizeFileInfo(array $stats)
    {
        return new FileAttributes(
            $stats['key'],
            $stats['fsize'] ?? null,
            null,
            isset($stats['putTime']) ? floor($stats['putTime'] / 10000000) : null,
            $stats['mimeType'] ?? null
        );
    }

    /**
     * 生成uploadToken
     * @param $options
     * @return array
     */
    public function getTempKeys($options = [])
    {
        $key            = $options['key'] ?? null;
        $expires        = $options['expires'] ?? 3600;
        $policy         = $options['policy'] ?? null;
        $strictPolicy   = $options['strictPolicy'] ?? true;
        $data['token']  = $this->getAuthManager()->uploadToken($this->config['bucket'], $key, $expires, $policy, $strictPolicy);
        $data['bucket'] = $this->config['bucket'];
        $data['domain'] = $this->config['cdn'];
        return $data;
    }

}
